# 解析mysql binlog文件，从中根据更新的脚本, 逆向调整为更新前的数据（指定字段）
import re

# 这段代码定义了一个函数 `parse_binlog`，用于解析 MySQL binlog 文件，提取所有针对 `kc_formal_contract` 表的 UPDATE 操作中 WHERE 子句涉及的字段值。其主要逻辑如下：
#
# 1. 读取 binlog 文件的所有行。
# 2. 遍历每一行，检测是否为 `UPDATE`、`WHERE` 或 `SET` 相关的标记行。
# 3. 当遇到 `### UPDATE` 且包含 `kc_formal_contract` 时，标记进入一次新的 UPDATE 语句，并准备收集 WHERE 条件字段。
# 4. 在 `### WHERE` 区块内，收集所有以 `###   @` 开头的字段行（即 WHERE 条件中的字段及其原始值）。
# 5. 当遇到 `### SET` 时，说明本次 UPDATE 结束，将收集到的 WHERE 字段保存到结果列表。
# 6. 最后返回所有收集到的 WHERE 字段列表，每个元素对应一次 UPDATE 的 WHERE 字段快照。
#
# 这样可以用于后续生成还原 SQL，把被 UPDATE 修改前的字段值恢复。
def parse_binlog(binlog_file_path):
    with open(binlog_file_path, 'r', encoding='utf-8') as f:
        lines = f.readlines()

    updates = []
    in_update = False
    in_where = False
    where_lines = []
    for line in lines:
        if line.startswith("### UPDATE") and "aj_process_record" in line:
            # 如果上一个WHERE未被SET终结，也要保存
            if where_lines:
                updates.append(where_lines)
                where_lines = []
            in_update = True
            in_where = False
        elif in_update and line.startswith("### WHERE"):
            in_where = True
        elif in_update and line.startswith("### SET"):
            in_where = False
            in_update = False
            updates.append(where_lines)
            where_lines = []
        elif in_where and line.startswith("###   @"):
            where_lines.append(line.strip())
    # 文件结尾如果还有未保存的where_lines
    if where_lines:
        updates.append(where_lines)
    return updates

def extract_fields(where_lines):
    # 字段编号与字段名映射
    field_map = {
        1: "id",
        6: "create_time"
    }
    values = {v: None for v in field_map.values()}
    regex = re.compile(r"@\s*(\d+)=('.*?'|NULL)")
    for line in where_lines:
        m = regex.search(line)
        if m:
            idx = int(m.group(1))
            val = m.group(2)
            if idx in field_map:
                if val == "NULL":
                    values[field_map[idx]] = None
                else:
                    values[field_map[idx]] = val.strip("'")
    return values

def generate_update_script(values):
    if not values["id"]:
        return None
    set_clause = []
    for k in ["create_time"]:
        v = values[k]
        if v is None:
            set_clause.append(f"{k} = NULL")
        else:
            set_clause.append(f"{k} = '{v}'")
    sql = f"UPDATE aj_process_record\nSET {', '.join(set_clause)}\nWHERE id = '{values['id']}';\n"
    return sql

def main():
    binlog_file_path = "data/binlog_ywzt_20250908_1410-1419.sql"
    output_file_path = "data/restore_update_ywzt.sql"
    updates = parse_binlog(binlog_file_path)
    with open(output_file_path, "w", encoding="utf-8") as fout:
        for where_lines in updates:
            values = extract_fields(where_lines)
            sql = generate_update_script(values)
            if sql:
                fout.write(sql + "\n")
    print(f"还原SQL已输出到: {output_file_path}")

if __name__ == "__main__":
    main()
